package tms.ui;

import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.StatusLineManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.widgets.Table;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ColumnPixelData;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.widgets.Label;
import org.eclipse.wb.swt.SWTResourceManager;
import swing2swt.layout.BorderLayout;
import tms.model.PeopleTable;
import tms.model.Person;
import tms.model.Task;
import tms.model.TaskTable;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.Separator;
import org.eclipse.wb.swt.ResourceManager;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.BeansObservables;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.core.databinding.observable.map.IObservableMap;
import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.core.databinding.property.value.IValueProperty;
import org.eclipse.jface.databinding.viewers.CellEditorProperties;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.core.databinding.beans.IBeanValueProperty;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.jface.databinding.viewers.ObservableValueEditingSupport;

/**
 * Main represents the GUI of the TMS
 * It provides the task and people tables, menu, statusbar and toolbar
 * 
 * @author Rolf Schmidt
 * @version 1.0
 */
public class Main extends ApplicationWindow {
	DataBindingContext m_bindingContext;
	private TaskTable m_tasks = TaskTable.getInstance();
	private PeopleTable m_people = PeopleTable.getInstance();

	// actions
	private Action actionHelpAbout;
	private Action actionFileExit;
	private Action actionFileExportPeople;
	private Action actionFileExportTasks;
	private Action actionFileOpen;

	// widths for table columns
	final static int WIDTH_ID = 20;
	final static int WIDTH_TITLE = 90;
	final static int WIDTH_DESCRIPTION = 120;
	final static int WIDTH_DURATION = 60;
	final static int WIDTH_DELIVERABLE = 140;
	final static int WIDTH_DEADLINE = 170;
	final static int WIDTH_PERSON = 90;
	final static int WIDTH_NAME = 90;
	final static int WIDTH_HOURS = 40;
	final static int WIDTH_TASKS = 20;

	// table viewers and columns
	private TableViewer tableViewerPeople;
	private TableViewer tableViewerTasks;
	private TableViewerColumn tableViewerColumnTitle;
	private TableViewerColumn tableViewerColumnDescription;
	private TableViewerColumn tableViewerColumnDuration;
	private TableViewerColumn tableViewerColumnDeliverable;
	private TableViewerColumn tableViewerColumnDeadline;
	private TableViewerColumn tableViewerColumnPerson;

	/**
	 * Create the application window.
	 */
	public Main() {
		super(null);

		ApplicationWindow.setDefaultImage(ResourceManager
				.getImage(ResourceManager.getImageDescriptor(
						Main.class, "/images/calendar-task.png")));
		createActions();
		addToolBar(SWT.FLAT | SWT.WRAP);
		addMenuBar();
		addStatusLine();
	}

	/**
	 * Create contents of the application window.
	 * 
	 * @param parent
	 */
	@Override
	protected Control createContents(Composite parent) {
		Composite container = new Composite(parent, SWT.NONE);
		FillLayout fl_container = new FillLayout(SWT.HORIZONTAL);
		fl_container.marginHeight = 5;
		fl_container.marginWidth = 5;
		container.setLayout(fl_container);

		// set up area for tables (tasks and people)
		Composite compositeTables = new Composite(container, SWT.BORDER);
		FillLayout fl_compositeTables = new FillLayout(SWT.HORIZONTAL);
		compositeTables.setLayout(fl_compositeTables);

		SashForm sashFormTables = new SashForm(compositeTables, SWT.VERTICAL);

		// set up tasks area
		Composite compositeTasks = new Composite(sashFormTables, SWT.NONE);
		compositeTasks.setLayout(new BorderLayout(0, 5));

		// tasks label
		Label lblTasks = new Label(compositeTasks, SWT.NONE);
		lblTasks.setLayoutData(BorderLayout.NORTH);
		lblTasks.setAlignment(SWT.CENTER);
		lblTasks.setFont(SWTResourceManager.getFont("Segoe UI", 16, SWT.BOLD
				| SWT.ITALIC));
		lblTasks.setText("Tasks");

		// tasks table
		Composite compositeTasksTable = new Composite(compositeTasks, SWT.NONE);
		TableColumnLayout tableColumnLayoutTasks = new TableColumnLayout();
		compositeTasksTable.setLayout(tableColumnLayoutTasks);

		tableViewerTasks = new TableViewer(compositeTasksTable, SWT.BORDER
				| SWT.FULL_SELECTION);
		Table tableTasks = tableViewerTasks.getTable();
		tableTasks.setLinesVisible(true);
		tableTasks.setHeaderVisible(true);
		tableTasks.setLayoutData(BorderLayout.CENTER);

		// tasks columns
		// ID
		TableViewerColumn tableViewerColumnId = new TableViewerColumn(
				tableViewerTasks, SWT.NONE);
		TableColumn tblclmnId = tableViewerColumnId.getColumn();
		tableColumnLayoutTasks.setColumnData(tblclmnId, new ColumnPixelData(
				WIDTH_ID, true, true));
		tblclmnId.setText("ID");

		tableViewerColumnTitle = new TableViewerColumn(tableViewerTasks,
				SWT.NONE);
		TableColumn tblclmnTitle = tableViewerColumnTitle.getColumn();
		tableColumnLayoutTasks.setColumnData(tblclmnTitle, new ColumnPixelData(
				WIDTH_TITLE, true, true));
		tblclmnTitle.setText("Title");

		// Description
		tableViewerColumnDescription = new TableViewerColumn(tableViewerTasks,
				SWT.NONE);
		TableColumn tblclmnDescription = tableViewerColumnDescription
				.getColumn();
		tableColumnLayoutTasks.setColumnData(tblclmnDescription,
				new ColumnPixelData(WIDTH_DESCRIPTION, true, true));
		tblclmnDescription.setText("Description");

		// Duration
		tableViewerColumnDuration = new TableViewerColumn(tableViewerTasks,
				SWT.NONE);
		TableColumn tblclmnDuration = tableViewerColumnDuration.getColumn();
		tableColumnLayoutTasks.setColumnData(tblclmnDuration,
				new ColumnPixelData(WIDTH_DURATION));
		tblclmnDuration.setText("Duration");

		// Deliverables
		tableViewerColumnDeliverable = new TableViewerColumn(tableViewerTasks,
				SWT.NONE);
		TableColumn tblclmnDeliverable = tableViewerColumnDeliverable
				.getColumn();
		tableColumnLayoutTasks.setColumnData(tblclmnDeliverable,
				new ColumnPixelData(WIDTH_DELIVERABLE, true, true));
		tblclmnDeliverable.setText("Deliverable");

		// Deadline
		tableViewerColumnDeadline = new TableViewerColumn(tableViewerTasks,
				SWT.NONE);
		TableColumn tblclmnDeadline = tableViewerColumnDeadline.getColumn();
		tableColumnLayoutTasks.setColumnData(tblclmnDeadline,
				new ColumnPixelData(WIDTH_DEADLINE, true, true));
		tblclmnDeadline.setText("Deadline");

		// Person
		tableViewerColumnPerson = new TableViewerColumn(tableViewerTasks,
				SWT.NONE);
		TableColumn tblclmnPerson = tableViewerColumnPerson.getColumn();
		tableColumnLayoutTasks.setColumnData(tblclmnPerson,
				new ColumnWeightData(1, 2, true));
		tblclmnPerson.setText("Person");

		// set up people area
		Composite compositePeople = new Composite(sashFormTables, SWT.NONE);
		compositePeople.setLayout(new BorderLayout(0, 5));

		// people label
		Label lblPeople = new Label(compositePeople, SWT.CENTER);
		lblPeople.setLayoutData(BorderLayout.NORTH);
		lblPeople.setFont(SWTResourceManager.getFont("Segoe UI", 16, SWT.BOLD
				| SWT.ITALIC));
		lblPeople.setText("People");

		// people table
		Composite compositePeopleTable = new Composite(compositePeople,
				SWT.NONE);
		TableColumnLayout tableColumnLayoutPeople = new TableColumnLayout();
		compositePeopleTable.setLayout(tableColumnLayoutPeople);

		tableViewerPeople = new TableViewer(compositePeopleTable, SWT.BORDER
				| SWT.FULL_SELECTION);
		Table tablePeople = tableViewerPeople.getTable();
		tablePeople.setLinesVisible(true);
		tablePeople.setHeaderVisible(true);
		tablePeople.setLayoutData(BorderLayout.CENTER);

		// people columns
		// name
		TableViewerColumn tableViewerColumnName = new TableViewerColumn(
				tableViewerPeople, SWT.NONE);
		TableColumn tblclmnName = tableViewerColumnName.getColumn();
		tableColumnLayoutPeople.setColumnData(tblclmnName, new ColumnPixelData(
				WIDTH_NAME, true, true));
		tblclmnName.setText("Name");

		// hours
		TableViewerColumn tableViewerColumnHours = new TableViewerColumn(
				tableViewerPeople, SWT.NONE);
		TableColumn tblclmnHours = tableViewerColumnHours.getColumn();
		tableColumnLayoutPeople.setColumnData(tblclmnHours,
				new ColumnPixelData(WIDTH_HOURS, true, true));
		tblclmnHours.setText("Hours");

		// task list
		TableViewerColumn tableViewerColumnTasks = new TableViewerColumn(
				tableViewerPeople, SWT.NONE);
		TableColumn tblclmnTasks = tableViewerColumnTasks.getColumn();
		tableColumnLayoutPeople.setColumnData(tblclmnTasks,
				new ColumnWeightData(WIDTH_TASKS));
		tblclmnTasks.setText("Tasks");

		sashFormTables.setWeights(new int[] { 1, 1 });
		m_bindingContext = initDataBindings();
		return container;
	}

	/**
	 * Create the actions.
	 */
	private void createActions() {
		// about
		{
			actionHelpAbout = new Action("About") {
				public void run() {
					About aboutDialog = new About(
							getShell());
					aboutDialog.open();
				}
			};
			actionHelpAbout
					.setToolTipText("Display information about the application");
			actionHelpAbout.setImageDescriptor(ResourceManager
					.getImageDescriptor(Main.class,
							"/images/information-frame.png"));
		}
		// exit application
		{
			actionFileExit = new Action("Exit") {
				public void run() {
					close();
				}
			};
			actionFileExit.setToolTipText("Exit the application");
			actionFileExit.setImageDescriptor(ResourceManager
					.getImageDescriptor(Main.class,
							"/images/control-power.png"));
		}
		// export people data
		{
			actionFileExportPeople = new Action("Export people") {
				public void run() {
					if (savePeopleFile()) {
						setStatus("Exported people file successfully");
					}
				}
			};
			actionFileExportPeople
					.setToolTipText("Export people information to a csv file");
			actionFileExportPeople.setImageDescriptor(ResourceManager
					.getImageDescriptor(Main.class,
							"/images/user-small-female-save.png"));
		}
		// export tasks data
		{
			actionFileExportTasks = new Action("Export tasks") {
				public void run() {
					if (saveTasksFile()) {
						setStatus("Exported tasks file successfully");
					}
				}
			};
			actionFileExportTasks.setToolTipText("Export tasks to a csv file");
			actionFileExportTasks.setImageDescriptor(ResourceManager
					.getImageDescriptor(Main.class,
							"/images/document-exports-tasks.png"));
		}
		// open project file
		{
			actionFileOpen = new Action("Open file...") {
				public void run() {
					if (openProjectFile()) {
						setStatus("Opened file successfully");
					}
				}
			};
			actionFileOpen
					.setToolTipText("Open a task management project file");
			actionFileOpen.setImageDescriptor(ResourceManager
					.getImageDescriptor(Main.class,
							"/images/folder-horizontal-open.png"));
		}
	}

	/**
	 * Create the menu manager.
	 * 
	 * @return the menu manager
	 */
	@Override
	protected MenuManager createMenuManager() {
		MenuManager menuManager = new MenuManager("menu");

		MenuManager menuManagerFile = new MenuManager("File");
		menuManager.add(menuManagerFile);
		menuManagerFile.add(actionFileOpen);
		menuManagerFile.add(actionFileExportTasks);
		menuManagerFile.add(actionFileExportPeople);
		menuManagerFile.add(new Separator());
		menuManagerFile.add(actionFileExit);

		MenuManager menuManagerHelp = new MenuManager("Help");
		menuManager.add(menuManagerHelp);
		menuManagerHelp.add(actionHelpAbout);
		return menuManager;
	}

	/**
	 * Create the toolbar manager.
	 * 
	 * @return the toolbar manager
	 */
	@Override
	protected ToolBarManager createToolBarManager(int style) {
		ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT | SWT.WRAP);
		toolBarManager.add(actionFileOpen);
		toolBarManager.add(actionFileExportTasks);
		toolBarManager.add(actionFileExportPeople);
		return toolBarManager;
	}

	/**
	 * Create the status line manager.
	 * 
	 * @return the status line manager
	 */
	@Override
	protected StatusLineManager createStatusLineManager() {
		StatusLineManager statusLineManager = new StatusLineManager();
		return statusLineManager;
	}

	/**
	 * Launch the application.
	 * 
	 * @param args
	 */
	public static void main(String args[]) {
		Display display = Display.getDefault();
		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
			public void run() {
				try {
					Main window = new Main();
					window.setBlockOnOpen(true);
					window.open();
					Display.getCurrent().dispose();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Configure the shell.
	 * 
	 * @param newShell
	 */
	@Override
	protected void configureShell(Shell newShell) {
		super.configureShell(newShell);

		newShell.setMinimumSize(700, 500);
		newShell.setSize(780, 580);
		newShell.setText("Task Management System - TMS");
		setStatus("COMP5541 Group 3");
	}

	/**
	 * Return the initial size of the window.
	 */
	@Override
	protected Point getInitialSize() {
		return new Point(650, 525);
	}

	/**
	 * Open a project file.
	 * 
	 * @return true if a file was opened successfully
	 */
	private boolean openProjectFile() {
		String selectedFile = getOpenFileNameXML();
		if (selectedFile == null) {
			return false;
		}
		m_tasks.openXMLFile(selectedFile);
		return true;
	}

	/**
	 * Save the people table to a file.
	 * 
	 * @return true if a file was saved successfully
	 */
	private boolean savePeopleFile() {
		String selectedFile = getSaveFileNameCSV("Save people file as");
		if (selectedFile == null) {
			return false;
		}
		m_people.saveCSVFile(selectedFile);
		return true;
	}

	/**
	 * Save the tasks table to a file.
	 * 
	 * @return true if a file was saved successfully
	 */
	private boolean saveTasksFile() {
		String selectedFile = getSaveFileNameCSV("Save tasks file as");
		if (selectedFile == null) {
			return false;
		}
		m_tasks.saveCSVFile(selectedFile);
		return true;
	}

	/**
	 * Display a save file dialog and retrieve filename
	 * 
	 * @return filename of selected file
	 */
	private String getSaveFileNameCSV(String title) {
		FileDialog fileDialog = new FileDialog(getShell(), SWT.SAVE);
		fileDialog.setText(title);
		fileDialog.setFilterExtensions(new String[] { "*.csv" });
		fileDialog.setFilterNames(new String[] { "CSV files (*.csv)" });
		// Open Dialog and save result of selection
		String selectedFile = fileDialog.open();
		if (selectedFile == null) {
			return null;
		}
		return selectedFile;
	}

	/**
	 * Display an open file dialog and retrieve filename
	 * 
	 * @return filename of selected file
	 */
	private String getOpenFileNameXML() {
		FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
		fileDialog.setText("Select project file");
		fileDialog.setFilterExtensions(new String[] { "*.xml" });
		fileDialog.setFilterNames(new String[] { "XML files (*.xml)" });
		// Open Dialog and save result of selection
		String selectedFile = fileDialog.open();
		if (selectedFile == null) {
			return null;
		}
		return selectedFile;
	}

	// the following is auto-generated; do not touch otherwise things will stop
	// working
	protected DataBindingContext initDataBindings() {
		DataBindingContext bindingContext = new DataBindingContext();
		// data-binding for task attributes
		ObservableListContentProvider listContentProvider = new ObservableListContentProvider();
		IObservableMap[] observeMaps = BeansObservables.observeMaps(
				listContentProvider.getKnownElements(), Task.class,
				new String[] { "id", "title", "description", "duration",
						"deliverable", "deadline", "person" });
		tableViewerTasks.setLabelProvider(new ObservableMapLabelProvider(
				observeMaps));
		tableViewerTasks.setContentProvider(listContentProvider);
		// data binding for task table as a whole
		IObservableList tasksTaskListObserveList = BeansObservables
				.observeList(Realm.getDefault(), m_tasks, "taskList");
		tableViewerTasks.setInput(tasksTaskListObserveList);
		// allow description in the task table to be editable and add data binding for that
		CellEditor cellEditor = new TextCellEditor(tableViewerTasks.getTable());
		IValueProperty cellEditorProperty = CellEditorProperties.control()
				.value(WidgetProperties.text(SWT.Modify));
		IBeanValueProperty valueProperty = BeanProperties.value("description");
		tableViewerColumnTitle.setEditingSupport(ObservableValueEditingSupport
				.create(tableViewerTasks, bindingContext, cellEditor,
						cellEditorProperty, valueProperty));
		//
		CellEditor cellEditor_1 = new TextCellEditor(
				tableViewerTasks.getTable());
		IValueProperty cellEditorProperty_1 = CellEditorProperties.control()
				.value(WidgetProperties.text(SWT.Modify));
		IBeanValueProperty valueProperty_1 = BeanProperties
				.value("description");
		tableViewerColumnDescription
				.setEditingSupport(ObservableValueEditingSupport.create(
						tableViewerTasks, bindingContext, cellEditor_1,
						cellEditorProperty_1, valueProperty_1));
		// allow duration in the task table to be editable and add data binding for that
		CellEditor cellEditor_2 = new TextCellEditor(
				tableViewerTasks.getTable());
		IValueProperty cellEditorProperty_2 = CellEditorProperties.control()
				.value(WidgetProperties.text(SWT.Modify));
		IBeanValueProperty valueProperty_2 = BeanProperties.value("duration");
		tableViewerColumnDuration
				.setEditingSupport(ObservableValueEditingSupport.create(
						tableViewerTasks, bindingContext, cellEditor_2,
						cellEditorProperty_2, valueProperty_2));
		//allow deliverable in the task table to be editable and add data binding for that
		CellEditor cellEditor_3 = new TextCellEditor(
				tableViewerTasks.getTable());
		IValueProperty cellEditorProperty_3 = CellEditorProperties.control()
				.value(WidgetProperties.text(SWT.Modify));
		IBeanValueProperty valueProperty_3 = BeanProperties
				.value("deliverable");
		tableViewerColumnDeliverable
				.setEditingSupport(ObservableValueEditingSupport.create(
						tableViewerTasks, bindingContext, cellEditor_3,
						cellEditorProperty_3, valueProperty_3));
		//allow deadline in the task table to be editable and add data binding for that
		CellEditor cellEditor_4 = new TextCellEditor(
				tableViewerTasks.getTable());
		IValueProperty cellEditorProperty_4 = CellEditorProperties.control()
				.value(WidgetProperties.text(SWT.Modify));
		IBeanValueProperty valueProperty_4 = BeanProperties.value("deadline");
		tableViewerColumnDeadline
				.setEditingSupport(ObservableValueEditingSupport.create(
						tableViewerTasks, bindingContext, cellEditor_4,
						cellEditorProperty_4, valueProperty_4));
		//allow person name in the task table to be editable and add data binding for that
		CellEditor cellEditor_5 = new TextCellEditor(
				tableViewerTasks.getTable());
		IValueProperty cellEditorProperty_5 = CellEditorProperties.control()
				.value(WidgetProperties.text(SWT.Modify));
		IBeanValueProperty valueProperty_5 = BeanProperties.value("person");
		tableViewerColumnPerson.setEditingSupport(ObservableValueEditingSupport
				.create(tableViewerTasks, bindingContext, cellEditor_5,
						cellEditorProperty_5, valueProperty_5));
		//data-binding for people table and person attributes
		ObservableListContentProvider listContentProvider_1 = new ObservableListContentProvider();
		IObservableMap[] observeMaps_1 = BeansObservables.observeMaps(
				listContentProvider_1.getKnownElements(), Person.class,
				new String[] { "name", "hours", "taskList" });
		tableViewerPeople.setLabelProvider(new ObservableMapLabelProvider(
				observeMaps_1));
		tableViewerPeople.setContentProvider(listContentProvider_1);
		//
		IObservableList peoplePersonListObserveList = BeansObservables
				.observeList(Realm.getDefault(), m_people, "personList");
		tableViewerPeople.setInput(peoplePersonListObserveList);
		//
		return bindingContext;
	}
}